import { ValueT } from "@/type";
import WatchValue from "../WatchValue";
import { BezierChanger } from "./BezierChanger";
import { LineChanger } from "./LineChanger";

/** 渐变回调函数 */
export type ChangeCallBack = (gradual: GradualNumber) => number;

/** 渐变类 */
export default class GradualNumber {
	private start: number;
	private value: number;
	private target: number;
	private length: number;
	private count!: WatchValue<number>;
	private progress: number;
	private space: number;
	private hasNext: boolean;
	private changer: ValueT<ChangeCallBack>;

	/** 初始值 */
	public get Start(): number {
		return this.start;
	}

	/** 当前值 */
	public get Value(): number {
		return this.value;
	}

	public set Value(to: number) {
		const { value, count, target } = this;
		if (target === to) { return; }
		this.start = value;
		this.target = to;
		this.length = to - value;
		this.space = this.length / (count.Value - 1);
		this.progress = 0;
		this.hasNext = true;
	}

	/** 目标值 */
	public get Target(): number {
		return this.target;
	}

	/** 变化长度 */
	public get Length(): number {
		return this.length;
	}

	/** 变化次数 */
	public get Count(): WatchValue<number> {
		return this.count;
	}

	public set Count(count: WatchValue<number>) {
		count.Add(this, () => {
			if (count.Value < 1) {
				count.Value = 1;
			} else {
				this.Value = this.target;
			}
		});
		this.count = count;
	}

	/** 变化进度 */
	public get Progress(): number {
		return this.progress;
	}

	/** 变化间距 */
	public get Space(): number {
		return this.space;
	}

	/** 是否可以变化 */
	public get HasNext(): boolean {
		return this.hasNext;
	}

	/** 渐变计算 */
	public get Changer(): ValueT<ChangeCallBack> {
		return this.changer;
	}

	public set Changer(changer: ValueT<ChangeCallBack>) {
		this.changer = changer;
	}

	/**
	 * 创建渐变类
	 * @param value 初始值
	 * @param count 变化次数
	 */
	public constructor(value: number, count: WatchValue<number> | number, changer: ValueT<ChangeCallBack>) {
		this.start = value;
		this.value = value;
		this.target = value;
		this.length = 0;
		this.space = 0;
		this.hasNext = false;
		this.Count = count instanceof WatchValue ? count : new WatchValue(count);
		this.progress = this.count.Value;
		this.changer = changer;
	}

	/** 触发变更 */
	public Trigger(): void {
		if (this.hasNext) {
			const { count, target, changer } = this;
			const progress = ++this.progress;
			if (progress >= count.Value - 1) {
				this.value = target;
				this.hasNext = false;
			} else {
				this.value = changer.Value(this);
			}
		}
	}

	/**
	 * 创建过渡类
	 * @param frame 过渡帧数
	 * @param transition 渐变方式 (line | ax,ay,bx,by)
	 */
	public static MakeChanger(transition: string): ChangeCallBack {
		const param = transition.split(",");
		if (param.length !== 4) {
			return LineChanger;
		} else {
			const [aX, aY, bX, bY] = param.map(str => parseFloat(str));
			return BezierChanger({ X: aX, Y: aY }, { X: bX, Y: bY }, 0.0001);
		}
	}

}
